// co
// all/race/allsettle/finnally
// queue/promiseQueue
// cacherequest
// 请求重试/请求超时/请求取消
// lazyMan/flow
// promisify
// 模拟微任务
// promiseLike

const { resolve } = require("path")

function co(gen){
    let g = gen()
    function next(params){
        const {value, done} = g.next(params)
        if(done) return value
        if(value.then){
            return value.then((val) => next(val))
        } else {
            return next(value)
        }
    }
    next()
}
co(function* (){
    let a = yield Promise.resolve(1)
    let b = yield Promise.resolve(2)
    console.log(a + b)
})

Promise.myAll = function(promises){
    let res = [];
    let count = 0
    return new Promise((resolve,reject) => {
        for(let i = 0; i<promises.length; i++){
            Promise.resolve(promises[i]).then(p => {
                res[i] = p
                count++
                if(count === promises.length){
                    resolve(res)
                }
            }).catch(err => reject(err))
        }
    })
}
Promise.myAll([
    Promise.resolve(1),
    Promise.resolve(2)
]).then(res => console.log(res))


Promise.myRace = function(promises){
    return new Promise((resolve, reject) => {
        for(let i = 0; i< promises.length; i++){
            Promise.resolve(promises[i]).then(p => {
                resolve(p)
            }).then(err => reject(err))
        }
    })
}
Promise.myRace([
    new Promise((resolve, reject) => setTimeout(resolve, 1000, "11")),
    new Promise((resolve, reject) => setTimeout(resolve, 2000, "22"))
]).then(res => console.log(res))


Promise.myAllSettle = function(promises){
    let res = promises.map((p) => {
        return Promise.resolve(p).then(val => {
            return {
                type: 'resolved',
                value: val
            }
        }).catch(err => {
            return {
                type: 'rejected',
                value: err
            }
        })
    })
    return Promise.all(res);
}
Promise.myAllSettle([
    Promise.resolve(1),
    '1',
    Promise.reject(new Error('1'))
]).then(res => console.log(res))


Promise.prototype.myFinally = function(cb){
    return this.then(res => {
        cb(res)
        return res;
    }).catch(err => {
        cb(err)
        throw err
    })
}
Promise.resolve(1).then(res => res).myFinally(res => console.log(res))


// 批量执行
class Queue {
    constructor(){
        this.list = []
    }
    add(fn){
        this.list.push(fn)
        return this;
    }
    async task(){
        for(let i = 0; i< this.list.length; i++){
            await this.list[i]()
        }
    }
}
const queue = new Queue();
var delay = (time) => new Promise(resolve => setTimeout(resolve, time))
queue.add(() => delay(1000).then(res => console.log('1')))
    .add(() => delay(2000).then(()=> console.log(2)))
    .task()




// 批量执行，可以限制兵法
class PromiseQueue {
    constructor(){
        this.list = [];
        this.count = 0;
        this.max = 2; 
    }
    add(fn){
        this.list.push(fn)
        this.next()
        return this;
    }
    next(){
        if(this.count >= this.max) return
        if(!this.list.length) return
        let fn = this.list.shift()
        let p = fn()
        this.count++
        p.then(this.complate.bind(this)).catch(this.complate.bind(this))
    }
    complate(){
        this.count--
        this.next()
    }
}
const queue2 = new PromiseQueue()
queue2.add(() => delay(1000).then(res => console.log('1')))
    .add(() => delay(1000).then(res => console.log('1')))




let list = []
let count = 0;
let max = 0;
let map = new Map()
function cacheRequest(url, successCallback, errorCallback){
    if(map.has(url)){
        return successCallback(map.get(url))
    }
    list.push({url, successCallback, errorCallback})
    processQueue()
}
async function processQueue(){
    if(this.count > this.max) return
    if(!this.list.length) return
    let res  = list.shift()
    let {url, successCallback, errorCallback} = res;
    count++
    try {
        let result = await fetch(url)
        let data  = await result.json()
        map.set(url, data)
        successCallback(data)
    } catch (error) {
        errorCallback(data)
    } finally {
        count--
        processQueue()
    }
}

// 接口重试
function request(url, max){
    return fetch(url).catch(err => max <= 0 ? Promise.reject(err) : request(url, max - 1))
}

// 请求超时
function createFetchWithTimeout(timeout){
    return function(url, options){
        return new Promise((resolve, reject) => {
            const controller = new AbortController()
            options = options || {}
            if(options.signal){
                options.signal.addEventListener('obort', () => {
                    controller.obort()
                })
            }
            options.signal = controller.signal

            const timeoutId = setTimeout(()=> {
                reject(new Error('fetch timeout'))
                controller.abort()
            }, timeout)

            fetch(url, options)
                .then(resolve, reject)
                .finally(() => {clearTimeout(timeoutId)})
        })
    }
}

// 请求取消
function createTask(time) {
    var _reject
    var _timer
    return {
      run() {
        return new Promise((resolve, reject) => {
          _reject = reject
          _timer = setTimeout(() => {
            console.log('123')
            resolve()
          }, time)
        }).catch(() => {
          console.log('msg')
        })
      },
      cancel() {
        clearTimeout(_timer)
        _reject({
          type: 'reject',
          msg: 'errr'
        })
      }
    }
  }
  let t = createTask(4000)
  t.run()
  
  setTimeout(() => {
    t.cancel()
  }, 1000)



class LazyMan {
  constructor(){
    this.list = []
    setTimeout( async() => {
      for(let i = 0; i<this.list.length; i++){
        let fn = this.list[i]
        await fn()
      }
    }, 0)
  }
  log(name){
    this.list.push(() => {console.log(name)})
    return this
  }
  sleep(delay){
    this.list.push(() => this._delay(delay))
    return this;
  }
  sleepFirst(delay){
    this.list.unshift(() => this._delay(delay))
    return this;
  }
  _delay(delay){
    return new Promise((resolve) => setTimeout(resolve, delay))
  }
}
new LazyMan('jiao').log('18888888').sleep(5000).log('28888888').sleepFirst(3000)


class LazyMan2 {
    constructor() {
        this.taskList = [];
        setTimeout(() => {
            this.next()
        }, 0)
    }
    log(msg) {
        let task = () => setTimeout(() => {
            console.log(msg)
            this.next()
        }, 0)
        this.taskList.push(task)
        return this;
    }
    sleep(time) {
        let task = () => setTimeout(() => {
            this.next()
        }, time)
        this.taskList.push(task)
        return this;
    }
    next() {
        let task = this.taskList.shift();
        task && task()
    }
}
// new lazyMan().log('1111').sleep(2000).log('22222')
// new LazyMan().eat().sleep(2000).eat().end()



function flow(promises){
     function next(i){
        if(!promises[i]) return
        let res = promises[i]()
        if(res && typeof res.then === 'function'){
            res.then(() => next(i + 1))
        } else {
            next(i+1)
        }
     }
     next(0)
}
flow([
    () => console.log('1'),
    () => console.log('2'),
    () => delay(1000).then(() => console.log('3')),
    () => delay(4000).then(() => console.log('4')),
    () => console.log('5'),
    () => console.log('6'),
])


// return2 promise 给原函数包装一个callback 放在最后
function promisify(originalFn) {
    return function (...args) {
        return new Promise((resolve, reject) => {
            function callback(err, ...result) {
                if (err) return reject(err)
                return resolve(result.length > 1 ? result : result[0])
            }
            // 在末尾传入callback
            originalFn.call(this, ...args, callback)
        })
    }
}
// 使用示例
const fs = require('fs');
const readFileAsync = promisify(fs.readFile);
readFileAsync('2023/原理手写4/test/test.txt', { encoding: 'utf8' })
    .then(contents => {
        console.log(contents, 'neiron');
    })
    .catch(error => {
        console.error('An error occurred:', error);
    });




function runMicroTask(func) {
    if (typeof Promise === 'function') {
        Promise.resolve().then(func)
        return
    }

    if (typeof MutationObserver === 'function') {
        var ob = new MutationObserver(func);
        let node = document.createTextNode('')
        ob.observe(node)
        node.data = 1
        return
    }

    if (typeof process.nextTick === 'function') {
        process.nextTick(func)
        return
    }
}


function promiseLike(promise) {
    return promise !== null
        && typeof promise === 'function'
        && typeof promise === 'object'
        && typeof promise.then === "function"
}

